
#uses "dpGroups"
#uses "sms_convert"
// -----------------------------------------------------------------------------
// SMS - Senden/Empfangen in PVSS II
//
// Implementierung von Funktionen zum Versenden und Empfangen von
// SMS (short message service) in PVSS II, Version 2.12.1
//
// Das SMS-Controlskript arbeitet alle definierten _SMS-Datenpunkte
// ab und fuehrt (wenn aktiviert) periodisch die Abfrage nach neuen
// SMS-Mitteilungen aus.
//
// The SMS message, as specified by the Etsi organization
// (documents GSM 03.40 and GSM 03.38)
//
// Aenderungen:
//   G. Ritz,   CPi, 21.11.2002: Neu
//   P. Zagler, CPi, 02.01.2003: 2 Sendeversuche je nach SMSC-Header
//   Stoklasek  ETM, 08.01.2003: Bei Sendefehler bleibt SMSC Header auf 0 & dpGetoptimierungen
//   Stoklasek  ETM, 21.01.2003: Optimierungen S35
//   Stoklasek  ETM, 13.02.2003: Nokia 30, Textsend
//   Stoklasek  ETM, 26.03.2003: Siemens TC35, PDU-Send/Receive
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Global
// -----------------------------------------------------------------------------
char      ctrl_Z = '\x1A';
unsigned  lst_lockid = 0;
bool      debugOn = TRUE;
bool      lowLevelDebug = TRUE;
// die gleiche Message nur einmal innerhalb von 30 Sekunden versenden !
string    sLastMessage;
time      tLastMessageTime;
int       iMessageBlockTime = 30;   // 30 seconds
// -----------------------------------------------------------------------------
// SMS Routinen (Level 2)
//
// Nur diese Routinen werden von main(), ... aufgerufen.
// -----------------------------------------------------------------------------
// ---
// SMS_Init
//
// error_code:
//   0    fuer Erfolg
//   -1   v24-Error
//   100  PIN-Code falsch
//   101  SMSC falsch
//   102  PDU-Mode nicht unterstuetzt
//   111  Speicherverknuepfen nicht unterstuetzt
// ---
void  SMS_Init
(
string      comport,
int         baudrate,
string      comsetting,
string      smsc,
string      sim,
int         &handle,
int         &error_code,
int         SmscHeaderFlag,
string      dp
)
{
  string      sms_status_report;
  dyn_string  sms_status_str;
  int         sms_status_max;
  string      SMS_AT_intern_command;
  int         pos1, pos2;
  string      sub;
  int         len;
  int         total;
  int         i;
  int         mem_count;
  string      mem_type;
  int         sms_status;
  int         v24_status;
  error_code = (-1);    // v24-Error
  v24_status = 0;
  sms_status = 0;
  // get handle
  if (handle != (-1))
  {
    DebugTN("v24Open() ...", handle, "... not opened.");
    return;
  }
  if (comport == "")
  {
    DebugTN("v24Open() ...", handle, "... not opened.");
    return;
  }
  handle = v24Open(comport, baudrate, comsetting);
  if ( lowLevelDebug) DebugTN(" v24Open",  handle , comport, baudrate, comsetting);
  if (handle == (-1))
  {
    DebugTN("v24Open() ...", handle, comport, baudrate, comsetting, "... not opened.");
    return;
  }
  DebugTN("v24Open() ...", handle, comport, baudrate, comsetting, "... opened.");
  SMS_AT_intern_command = "E0";
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  // PIN Anfrage
  // ["v24Read() ..."][""]["+CPIN: READY"]["OK"]["... 20 Bytes read."]
  SMS_AT_intern_command = "+CPIN?";
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    // hier auch v24-Error zurueckgegeben.
    // COM-Schnittstelle zwar vorhanden, aber kein SMS-Geraet.
    error_code = (-1);    // v24-Error
    return;
  }
  if (strpos(sms_status_str[2], "READY") == (-1))
  {
    // set PIN-Code
    // ["v24Write() ..."]["AT+CPIN=7695"]["... 13 Bytes written."]
    SMS_AT_intern_command = "+CPIN=" + "\""+sim+ "\"";
    SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
    if (v24_status != 1)
    {
      return;
    }
    sms_status = get_sms_status(sms_status_report);
    // DebugTN("v24_status:" + v24_status, "sms_status:" + sms_status);
    if (sms_status != 1)
    {
    DebugN("Error: PIN-Code falsch");
      error_code = 100;   // Error: PIN-Code falsch
      return;
    }
  }
  SMS_AT_intern_command = "Z";           // Dummy commando for delay
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  delay(1);
  // set Echo off
  // ["v24Write() ..."]["ATE0"]["... 5 Bytes written."]
  SMS_AT_intern_command = "E0";
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    // hier auch v24-Error zurueckgegeben.
    // COM-Schnittstelle zwar vorhanden, aber kein SMS-Geraet.
    error_code = (-1);    // v24-Error
    return;
  }
  // SMSC-number setzen
  // ["v24Write() ..."]["AT+CSCA=+43676021"]["... 18 Bytes written."]
  // ["v24Read() ..."][""]["+CSCA: \"+43676021\" , 145"]["OK"]["... 32 Bytes read."]
  SMS_AT_intern_command = "+CSCA=" + "\""+ smsc +"\",145";
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
      error_code = 101;   // Error: SMSC falsch
      return;
  }
  {
      string mode;
    // find out if Text or PDU mode !!
    SMS_AT_intern_command = "+CMGF=?";
    SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
    if (v24_status != 1)
    {
      return;
    }
    // answer:+CMGF: (0,1)
    // 1st: generate 3 parts
    if ( (strpos ( sms_status_str[2] , "0") < 0)) // Textmode, wenn Endgeraet keinen PDU-Modus kann
     {
       dpSet(dp+".SmscHeader:_original.._value", 3);        // writing in textmode
       mode = "1";
     }
    else
      mode = "0";
    // PDU-Mode setzen
    // ["v24Write() ..."]["AT+CMGF=0"]["... 10 Bytes written."]
    SMS_AT_intern_command = "+CMGF="+mode;
    SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
    if (v24_status != 1)
    {
      return;
    }
    sms_status = get_sms_status(sms_status_report);
    if (sms_status != 1)
    {
        error_code = 102;   // Error: PDU-Mode nicht unterstuetzt
        return;
    }
  }
  // Zugriff auf s�mtlichen vorhandenen Speicher erm�glichen.
  SMS_AT_intern_command = "+CPMS=?";
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
      error_code = 111;   // Error: Speicherverknuepfen nicht unterstuetzt
      return;
  }
/*
  mem_count = 0;
  mem_type = "MT";
  pos1 = strpos(sms_status_str[2], mem_type);
  if (pos1 < 6)            // kein MT Speicher vorhanden
  {
    mem_type = "SM";
  }
  // sub = sms_status_str[2];
  sub = substr(sms_status_str[2], 6);
  while (1)
  {
    pos1 = strpos(sub, mem_type);
    if (pos1 < 0)            // kein Speicherbereich mehr gefunden
    {
      break;
    }
    sub = substr(sub, pos1 + 2);
    mem_count++;
  }
  SMS_AT_intern_command = "+CPMS=";
  for (i = 1; i <= mem_count; i++)
  {
    if (i == mem_count)
      SMS_AT_intern_command = SMS_AT_intern_command + "\"" + mem_type + "\"";
    else
      SMS_AT_intern_command = SMS_AT_intern_command + "\"" + mem_type + "\",";
  }
*/
  // new memory selecton Stoklasek 13.2.03
  {
      dyn_string dsMemTypes = makeDynString("MT", "SM", "ME");
      string sTempString ;
      dyn_string dsTempString ;
      int i, j;
  // answer: +CPMS: (\"ME\",\"SM\"),(\"ME\",\"SM\"),(\"MT\")
  // 1st: generate 3 parts
     dsTempString = strsplit(sms_status_str[2], ")");
     for ( i = 1; i<= dynlen(  dsTempString); i++)
     {
       for (j=1; j<=dynlen( dsMemTypes); j++)
       {
         if ( strpos( dsTempString[i], dsMemTypes[j]) > 0)     // found memtype
         {
            if (sTempString != "")                             // add "," if not first element
               sTempString += ",";
            sTempString += "\""+dsMemTypes[j]+"\"";            // add mentype with higest priority
            j=dynlen(dsMemTypes);                                             // end this search
         }
       }
     }
     SMS_AT_intern_command =  "+CPMS="+ sTempString;
  }
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
      error_code = 111;   // Error: Speicherverknuepfen nicht unterstuetzt
      return;
  }
  error_code = 0;     // OK
}
// ---
// SMS_Exit
//
// error_code:
//   0    fuer Erfolg
//   -1   v24-Error
// ---
void  SMS_Exit
(
int         &handle,
int         &error_code
)
{
  int         v24_status;
  error_code = (-1);    // v24-Error
  if (handle == (-1))
  {
    DebugTN("v24Close() ..", handle, "... not closed.");
    return;
  }
  v24_status = v24Close(handle);
  if ( lowLevelDebug) DebugTN(" v24Close",  handle , v24_status);
  delay(5);
  if (v24_status == (-1))
  {
    DebugTN("v24Close() ..", handle, "... not closed.");
    return;
  }
  DebugTN("v24Close() ..", handle, "... closed.");
  handle = (-1);
  error_code = 0;     // OK
}
// ---
// SMS_Receive
//
// error_code:
//   0    fuer Erfolg
//   -1   v24-Error
//   103  SMS loeschen nicht moeglich
//   105  SMS lesen nicht moeglich
//   200  keine SMS zum Lesen da
// ---
void  SMS_Receive
(
int         handle,
int         autodelete,
int         SmscHeaderFlag,
string      &phone_number,
string      &message,
string      &msg_time,
int         &error_code
)
{
  string      sms_status_report;
  dyn_string  sms_status_str;
  int         sms_status_max;
  string      SMS_AT_intern_command;
  int         pos1, pos2;
  string      sub;
  //
  int         smsc_info_len;
  int         smsc_head_len;
  //
  int         pdutype;
  int         len;
  int         typeofnumber;
  //
  int         pid;
  int         dcs;
  string      scts;
  int         udl;
  //
  int         len2;
  //
  int         stat;
  //
  int         sms_status;
  int         v24_status;
  //
  int         used;
  int         total;
  int         i;
  //
  string      subl;
  int         lenl;
  //
  int         headlen;
  string      withouthead;
  int         longlen;
  //
  int         posn;   // fuer alphanumeric 7-bit phone number, z.B. www.sms.at
  //
  int         iCountSMS ;
  error_code = (-1);    // v24-Error
  sms_status = 0;
  phone_number = "";
  message = "";
  msg_time = "";
  sms_status = 0;
  SMS_AT_intern(handle, "E0", sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);   // ECHO OFF
  SMS_AT_intern_command = "+CPMS?";   // query message storage
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    error_code = 105;   // Error: SMS lesen nicht moeglich
    return;
  }
  pos1 = strpos(sms_status_str[2], ",");
  len = strlen(sms_status_str[2]);
  sub = substr(sms_status_str[2], pos1 + 1, len - pos1 - 1);
  sscanf(sub, "%d", used);
  pos2 = strpos(sub, ",");
  len = strlen(sub);
  sub = substr(sub, pos2 + 1, len - pos2 - 1);
  sscanf(sub, "%d", total);
  if (used == 0)   // keine eingegangene SMS da
  {
    error_code = 200;   // Warning: no text to read
    return;
  }
  for(i = 1; i <= total; i++)
  {
    SMS_AT_intern_command = "+CMGR=" + i;   // read stored message in location i
    SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
    if (v24_status != 1)
    {
    DebugN("Error on com port.");
      return;
    }
    sms_status = get_sms_status(sms_status_report);
    if (sms_status != 1)
    {
      error_code = 105;   // Error: SMS lesen nicht moeglich
      return;
    }
    if ( iCountSMS >= used)  // Found SMS message witch can not be read -> add counter -> found iCountSMS > uses -> end polling
       break;
    if ( dynlen( sms_status_str ) < 3)
    {
        continue;
    }
    // !! auf <stat> received testen.
    // !! unread und read werden nicht unterschieden.
    //
    pos1 = strpos(sms_status_str[2], ":");
    len = strlen(sms_status_str[2]);
    sub = substr(sms_status_str[2], pos1 + 1, len - pos1 - 1);
    pos2 = strpos(sub, ",");
    subl = substr(sub, pos2 + 2, strlen(sub) - pos2 - 2);
    sscanf(subl, "%d", lenl);
    sub = substr(sub, 0, pos2);
    sscanf(sub, "%d", stat);
    if (stat > 1)
    {
       iCountSMS ++;
       continue;   // <stat> != received
    }
    if (lenl == 0)
    {
      continue;   // Laenge == 0, d.h. Speicherplatz leer.
    }
    // auswerten
    //
    if (SmscHeaderFlag == 1)
    {
      smsc_head_len = 0;    // (Siemens M1 Fehler)
    }
    else
    {
      sub = substr(sms_status_str[3], 0, 2);
      sscanf(sub, "%x", smsc_info_len);
      smsc_head_len = 2 + smsc_info_len * 2;
    }
    sub = substr(sms_status_str[3], smsc_head_len + 0, 2);
    sscanf(sub, "%x", pdutype);
    sub = substr(sms_status_str[3], smsc_head_len + 2, 2);
    sscanf(sub, "%x", len);
    len2 = ((len + 1) / 2) * 2;
    sub = substr(sms_status_str[3], smsc_head_len + 4, 2);
    sscanf(sub, "%x", typeofnumber);
    if (typeofnumber == 0xD0)
    {
      sub = substr(sms_status_str[3], smsc_head_len + 6, len);
      phone_number = decode_ud_7bit(sub);
      posn = strpos(phone_number, "@");
      if (posn > 0)
      {
        phone_number = substr(phone_number, 0, posn);
      }
    }
    else
    {
      sub = substr(sms_status_str[3], smsc_head_len + 6, len2);
      phone_number = decode_bcd(sub);
    }
    sub = substr(sms_status_str[3], smsc_head_len + 6 + len2 + 0, 2);
    sscanf(sub, "%x", pid);
    sub = substr(sms_status_str[3], smsc_head_len + 6 + len2 + 2, 2);
    sscanf(sub, "%x", dcs);
    sub = substr(sms_status_str[3], smsc_head_len + 6 + len2 + 4, 14);
    scts = decode_bcd(sub);
    msg_time = substr(scts, 0, 2) + "." + substr(scts, 2, 2) + "." + substr(scts, 4, 2) + " "
            + substr(scts, 6, 2) + ":" + substr(scts, 8, 2) + ":" + substr(scts, 10, 2);
    // nicht 2 sondern 7*2 sollten vorher gelesen werden.....
    sub = substr(sms_status_str[3], smsc_head_len + 6 + len2 + 18, 2);
    sscanf(sub, "%x", udl);
    // if (dcs == 0x00)
    if ((dcs == 0x00) || (dcs == 0x11) || (dcs == 0xF1))
    {
      // 7-bit coding
      sub = substr(sms_status_str[3], smsc_head_len + 6 + len2 + 20, ((udl * 7 + 7) / 8) * 2);
      message = decode_ud_7bit(sub);
      message = substr(message, 0, udl);                  // NEU
    }
    else
    {
      // 8-bit coding
      sub = substr(sms_status_str[3], smsc_head_len + 6 + len2 + 20, udl * 2);
      message = decode_ud_8bit(sub);
    }
    if (autodelete)
    {
      delay(2);
      SMS_AT_intern_command = "+CMGD=" + i;
      SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
//      if (v24_status != 1)
//      {
//        return;
//      }
//      sms_status = get_sms_status(sms_status_report);
//      if (sms_status != 1)
//      {
//        error_code = 103;   // Error: SMS loeschen nicht moeglich
//        return;
//      }
    }
    break;
  }
  if (i == total + 1)   // keine eingegangene SMS da
  {
    error_code = 200;   // Warning: keine SMS zum Lesen da
    return;
  }
  error_code = 0;     // OK
}
// ---
// SMS_Send
//
// error_code:
//   0    fuer Erfolg
//   -1   v24-Error
//   106  SMS senden nicht moeglich
// ---
void  SMS_Send
(
int         handle,
string      phone_number,
string      message,
int         SmscHeaderFlag,
int         &error_code
)
{
  string      sms_status_report;
  dyn_string  sms_status_str;
  int         sms_status_max;
  string      SMS_AT_intern_command;
  int         pos1, pos2;
  string      sub;
  //
  int         pdutype;
  int         len;
  int         typeofnumber;
  //
  int         pid;
  int         dcs;
  string      scts;
  int         udl;
  //
  int         len2;
  //
  int         stat;
  //
  int         sms_status;
  int         v24_status;
  //
  int         total;
  int         i;
  //
  string      full_cmd;
  string      full_msg;
  //
  int         len_msg;
  //
  string      encoded;
  //
  string      sms_command;
  error_code = (-1);    // v24-Error
  sms_status = 0;
if ( SmscHeaderFlag == 3)
{
  // SMS_AT_intern_command = "+CMGF=1";   // text mode
  // SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  SMS_AT_intern_command = "+CMGS=\""+phone_number+"\",145";  // number
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    error_code = 106;   // Error: SMS senden nicht moeglich
    return;
  }
  SMS_AT_intern_command = message + ctrl_Z;  // message + ctrl Z withou AT  witht doubleread
  SMS_IO(handle, SMS_AT_intern_command,1, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    error_code = 106;   // Error: SMS senden nicht moeglich
    return;
  }
  // SMS_AT_intern_command = "+CMGF=0";   // pdu mode
  // SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  error_code = 0;     // OK
}
else
{
  if (SmscHeaderFlag == 1)
  {
    full_msg = "1100";    // PDU-type + MR  (Siemens M1 Fehler)
  }
  else
  {
    full_msg = "001100";  // Length of SMSC information + PDU-type + MR
  }
  strreplace(phone_number, "+", "");  // neu
  len = strlen(phone_number);
  sprintf(sub, "%0.2X", len);
  full_msg = full_msg + sub;    // + len
  full_msg = full_msg + "91";   // + typeofnumber (international)
  encoded = encode_bcd(phone_number);
  full_msg = full_msg + encoded;    // + phone_number (encoded)
  full_msg = full_msg + "00";   // + PID
  //full_msg = full_msg + "F6";   // + DCS (8-bit coding)
  //full_msg = full_msg + "00";   // + DCS (7-bit coding)
  full_msg = full_msg + "08";   // + TP-DCS (16-bit coding) UCS2
  full_msg = full_msg + "AA";   // + VP (four days)
  /*
   00 1100 0D 91 685110108723F3 00 00 AA 05 E8329BFD06 = hello
   00 1100 0D 91 685110108723F3 00 08 AA 05 E8329BFD06 = hello
    00 - SMS Center length . 00 = store in card.
    1100
    0011000D91 685110108723F3 0008 AA 08 003F003F003F003F
*/
 //   DebugN("the message is been translate:",message);
      blob blobMsg;
      // ANSI_Unicode(message,blobMsg);
      system(getPath(BIN_REL_PATH) + "ANSI_UCS2.exe \"" + message +"\"", blobMsg);
      // DebugN("UCS2 recode,message->blogMsg", message, blobMsg);
      string sTemp = (string)blobMsg;
      encoded = "";
      for(int i=0; i<strlen(sTemp); i+=4)
      {
      //  s3 = s3 + s2[i];
        encoded = encoded + sTemp[i+2] + sTemp[i+3] + sTemp[i] + sTemp[i+1];
      }
//  udl = strlen(encoded);    // ?? fuer 7-bit coding, OK 8-bit coding
  udl = strlen(encoded)/2;    // ;;;NOW IS 16-BIT BY LZG.
  sprintf(sub, "%0.2X", udl);
  full_msg = full_msg + sub;    // + UDL
//  encoded = encode_ud_7bit(message);
  full_msg = full_msg + encoded;    // + message (encoded)
  len = strlen(full_msg);
  // laut Docs: 140 is the maximum length (in byte) of the PDU
  //            * 2 = 280
  // laut Test: 290 fuer das ganze Zeug.
  //            d.h. 129 chars bei 8-bit coding.
  if (len >= 290)
  {
    error_code = 106;   // Error: SMS senden nicht moeglich
    return;
  }
  // SMS mit korrekter L�nge anmelden
  sprintf(sub, "%d", len/2-1); // correct PDU lengh
  //full_cmd = "+CMGS=140";     // 140 is the maximum length (in byte) of the PDU
  full_cmd = "+CMGS=" + sub;
  SMS_AT_intern_command = full_cmd;
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    error_code = 106;   // Error: SMS senden nicht moeglich
    return;
  }
  sms_command = full_msg + ctrl_Z;
  // double_read = 1, wichtig fuer richtige Fehlermeldung
  SMS_IO(handle, sms_command, 1, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    error_code = 106;   // Error: SMS senden nicht moeglich
    return;
  }
  error_code = 0;     // OK
}
}
// ---
// SMS_Quality
//
// error_code:
//   0    fuer Erfolg
//   -1   v24-Error
//   104  Quality-Abfrage geht nicht
// ---
void  SMS_Quality
(
int         handle,
int         &receive_level,
int         &error_code,
int         SmscHeaderFlag
)
{
  string      sms_status_report;
  dyn_string  sms_status_str;
  int         sms_status_max;
  string      SMS_AT_intern_command = "+CSQ";   // Siemens GSM Module M1, User Guide, page 42
  int         pos1, pos2, pos3;
  string      sub;
  int         receive_errors;
  int         sms_status;
  int         v24_status;
  string      qualityString;
  int         i;
  error_code = (-1);    // v24-Error
  sms_status = 0;
  receive_level = 99;   // Unknown or cannot be detected
  receive_errors = 99;  // Unknown or cannot be detected
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    receive_level = Normalize_Quality(receive_level);
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    error_code = 104;   // Error: Quality-Abfrage geht nicht
    receive_level = Normalize_Quality(receive_level);
    return;
  }
  for ( i= 1; i<= dynlen( sms_status_str); i++)             // find corect position in dynstring (regulas 2 or 3)
  {
    if ( strpos( sms_status_str[i],"+CSQ") >= 0)
      qualityString = sms_status_str[i];
  }
  pos1 = strpos(qualityString, ":");
  pos2 = strpos(qualityString, ",");
  pos3 = strlen(qualityString);
  sub = substr(qualityString, pos1 + 1, pos2 - pos1 - 1);
  sscanf(sub, "%d", receive_level);
  sub = substr(qualityString, pos2 + 1, pos3 - pos2 - 1);
  sscanf(sub, "%d", receive_errors);
  receive_level = Normalize_Quality(receive_level);
  error_code = 0;     // OK
}
// ---
// SMS_Delete
//
// error_code:
//   0    fuer Erfolg
//   -1   v24-Error
//   103  SMS loeschen nicht moeglich
// ---
void  SMS_Delete
(
int         handle,
int         &error_code,
int         SmscHeaderFlag
)
{
  string      sms_status_report;
  dyn_string  sms_status_str;
  int         sms_status_max;
  string      SMS_AT_intern_command;
  int         pos1, pos2;
  string      sub;
  int         len;
  int         total;
  int         i;
  int         sms_status;
  int         v24_status;
  error_code = (-1);    // v24-Error
  sms_status = 0;
  SMS_AT_intern_command = "+CPMS?";   // query message storage
  SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  sms_status = get_sms_status(sms_status_report);
  if (sms_status != 1)
  {
    error_code = 103;   // Error: SMS loeschen nicht moeglich
    return;
  }
  pos1 = strpos(sms_status_str[2], ",");
  len = strlen(sms_status_str[2]);
  sub = substr(sms_status_str[2], pos1 + 1, len - pos1 - 1);
  pos2 = strpos(sub, ",");
  len = strlen(sub);
  sub = substr(sub, pos2 + 1, len - pos2 - 1);
  sscanf(sub, "%d", total);
  for(i = 1; i <= total; i++)
  {
    SMS_AT_intern_command = "+CMGD=" + i;
    // DebugTN("SMS_AT_intern_command = ", SMS_AT_intern_command);
    SMS_AT_intern(handle, SMS_AT_intern_command, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
    if (v24_status != 1)
    {
      return;
    }
    sms_status = get_sms_status(sms_status_report);
    if (sms_status != 1)
    {
      error_code = 103;   // Error: SMS loeschen nicht moeglich
      return;
    }
  }
  error_code = 0;     // OK
}
// ---
// SMS_AT
//
// error_code:
//   0    fuer Erfolg
//   -1   v24-Error
// ---
void  SMS_AT
(
int         handle,
string      SMS_AT_command,
dyn_string  &sms_status_str,
int         &error_code,
int         SmscHeaderFlag
)
{
  string      sms_status_report;
  int         sms_status_max;
  string      sms_command = "AT" + SMS_AT_command + "\r";
  int         v24_status;
  error_code = (-1);    // v24-Error
  SMS_IO(handle, sms_command, 0, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
  if (v24_status != 1)
  {
    return;
  }
  error_code = 0;   // OK
}
// -----------------------------------------------------------------------------
// SMS Routinen (Level 1)
//
// MUeSSEN void sein, da nur void v24Read(), delay() usw. zulaesst.
// -----------------------------------------------------------------------------
// ---
// get_sms_status
// ---
int get_sms_status
(
string  sms_status_report
)
{
  dyn_string  sms_status_str;
  int         max;
  int         i;
  sms_status_str = strsplit(sms_status_report, "\n");
  max = dynlen(sms_status_str);
  if (max < 1)
  {
    return (-1);    // should never happen
  }
  if ((strpos(sms_status_str[max], "OK") != 0) &&
      (strpos(sms_status_str[max], "> ") != 0) &&
      (strpos(sms_status_str[max], "+CMS ERROR: 321") < 0))
  {
    return 0;       // ERROR
  }
  dpSet("_SMS_1.Status:_original.._value",0);      //Note : lzg: I am very lazy, I don't want to modify it as 'dp' variable.
  return 1;         // OK
}
// ---
// SMS_AT_intern
// ---
void  SMS_AT_intern
(
int         handle,
string      SMS_AT_intern_command,
string      &sms_status_report,
dyn_string  &sms_status_str,
int         &sms_status_max,
int         &v24_status,
int         SmscHeaderFlag
)
{
  string  sms_command = "AT" + SMS_AT_intern_command + "\r";
  SMS_IO(handle, sms_command, 0, sms_status_report, sms_status_str, sms_status_max, v24_status, SmscHeaderFlag);
}
// ---
// SMS_IO
// ---
void  SMS_IO
(
int         handle,
string      sms_command,
int         double_read,
string      &sms_status_report,
dyn_string  &sms_status_str,
int         &sms_status_max,
int         &v24_status,
int         SmscHeaderFlag
)
{
  int     i;
  int     writeWork, read;
  //
  string  helper;
  int     sms_status;
  string sTemp;
  int iCounter;
  //
  sms_status_report = "";
  sms_status_max = dynlen(sms_status_str);
  for (i = 1; i < sms_status_max; i++)
  {
    dynRemove(sms_status_str, i);
  }
  sms_status_max = dynlen(sms_status_str);
  v24_status = 0;
  // NEU: bei Siemens GSM Module M1 aber nicht notwendig
  if (SmscHeaderFlag > 1 && sms_command[strlen(sms_command) - 1] != ctrl_Z)
     sms_command += "\r\n";   // Fehlerbehebung Stoklasek
  if(debugOn) DebugN(sms_command);
  writeWork = v24Write(handle, sms_command );
  if ( lowLevelDebug) DebugTN(" v24Write", handle, sms_command, double_read, SmscHeaderFlag, writeWork);
  // writeWork = v24Write(handle, sms_command);
  if (writeWork == (-1))
  {
    DebugTN("v24Write() ..", handle, sms_command, "... FAILED.");
    // v24_status = 0;
    return;
  }
  i = strlen(sms_command) - 1;
  if (debugOn)
  {
    if ((i >= 0) && (sms_command[i] == ctrl_Z))
      DebugTN("v24Write() ..", handle, substr(sms_command, 0, i) + "<ctrl-Z>", "... " + writeWork + " Bytes written.");
    else
      DebugTN("v24Write() ..", handle, sms_command, "... " + writeWork + " Bytes written.");
  }
  delay(1);     // WICHTIG !!
  iCounter = 0;
  do
  {
    sTemp = "";
    read = v24Read(handle, sTemp, 1);
    sms_status_report += sTemp;
    if ( sms_status_report == "")       // 10 * 2 Sekunden Delay wenn garkeine Antwort
    {
      iCounter++;
      if ( iCounter > 20)
         sTemp = " ";   // dummy
    }
  }  while (strlen(sTemp) == 0);   // neue Leseroutine Stoklasek
  if ( lowLevelDebug) DebugTN(" v24Read", handle, sms_status_report);
  delay(1);     // WICHTIG !!
  if (double_read)
  {
      sms_status = get_sms_status(sms_status_report); // test if OK ; if not doubleread !!
      if ( sms_status != 1 )
      {
        sms_status_report = "";
        do
        {
          sTemp = "";
          read = v24Read(handle, sTemp, 2);
          sms_status_report += sTemp;
        }   while (strlen(sTemp) > 0);   // neue Leseroutine Stoklasek
        if ( lowLevelDebug) DebugTN(" v24Read", handle, sms_status_report);
      }
      else
           if ( lowLevelDebug) DebugTN(" v24Read double cancled");
  }
  if (strlen(sms_status_report) == 0)
  {
    DebugTN("v24Read() ...", handle, "... FAILED.");
    // v24_status = 0;
    return;
  }
  sms_status_str = strsplit(sms_status_report, "\n");
  sms_status_max = dynlen(sms_status_str);
  if (debugOn)
  {
    if (sms_status_max == 1)
      DebugTN("v24Read() ...", handle, sms_status_str[1], "... " + strlen(sms_status_report) + " Bytes read.");
    else if (sms_status_max == 2)
      DebugTN("v24Read() ...", handle, sms_status_str[1], sms_status_str[2], "... " + strlen(sms_status_report) + " Bytes read.");
    else if (sms_status_max == 3)
      DebugTN("v24Read() ...", handle, sms_status_str[1], sms_status_str[2], sms_status_str[3], "... " + strlen(sms_status_report) + " Bytes read.");
    else if (sms_status_max == 4)
      DebugTN("v24Read() ...", handle, sms_status_str[1], sms_status_str[2],
        sms_status_str[3], sms_status_str[4], "... " + strlen(sms_status_report) + " Bytes read.");
    else
      DebugTN("v24Read() ...", handle, sms_status_report, "... " + strlen(sms_status_report) + " Bytes read.");
  }
  v24_status = 1;
}
// -----------------------------------------------------------------------------
// Konvertierung UD (User Data)
// -----------------------------------------------------------------------------
// ---
// encode_ud_7bit
// ---
string  encode_ud_7bit
(
string  str
)
{
  string  ud_str = "";
  int     len, i;
  int     c;
  string  bin1 = "";
  string  bin2;
  string  join;
  string  sub;
  int     l;
  bit32   b;
  len = strlen(str);
  for (i = 0; i < len + 1; i++)
  {
    if (i == len)
    {
      bin2 = "0000000";   // wichtig: nur 7 Stellen; Endebehandlung
    }
    else
    {
      c = str[i];
      c = recode_to_7bit(c);                // NEU: Zeichenkonvertierung
      bin2 = substr(int_to_bin(c), 1, 7);   // nur 7 bit
    }
    join = bin2 + bin1;
    l = strlen(join);
    if (l >= 8)
    {
      sub = substr(join, l - 8, 8);
      setPattern(b, sub);
      bin1 = substr(join, 0, l - 8);        // auf len 0 reagieren ist ok.
      bin2 = "";
      c = b;
      sprintf(ud_str, "%s%0.2X", ud_str, c);
    }
    else
    {
      bin1 = bin2;        // Anfangbehandlung und Behandlung Vielfache von 8
      bin2 = "";
    }
  }
  return ud_str;
}
// ---
// get7bitchar
// ---
int get7bitchar
(
string  &str1,
string  &str2
)
{
  string  concats;
  int     len;
  string  pattern;
  bit32   b = 0;
  int     c = 0;          // nur zur Sicherheit
  concats = str2 + str1;  // Reihenfolge ist wichtig
  len = strlen(concats);
  pattern = substr(concats, len - 7, 7);
  setPattern(b, pattern);
  str1 = "";              // muss jetzt leer sein
  str2 = substr(concats, 0, len - 7);
  c = b;
  c = recode_to_8bit(c);                    // NEU: Zeichenkonvertierung
  return c;
}
// ---
// int_to_bin
// ---
string  int_to_bin
(
int     c
)
{
  string  str = "";
  int     i;
  bit32   b;
  b = c;
  for (i = 7; i >= 0; i--)
  {
    if (getBit(b, i))
    {
      sprintf(str, "%s%c", str, '1');
    }
    else
    {
      sprintf(str, "%s%c", str, '0');
    }
  }
  return str;
}
// ---
// decode_ud_7bit
// ---
string  decode_ud_7bit
(
string  ud_str
)
{
  string  str = "";
  int     len, i;
  string  hex;
  int     c;
  string  bin1 = "";
  string  bin2;
  string s;
  len = strlen(ud_str);
  for (i = 0; i < len; i += 2)
  {
    hex = substr(ud_str, i, 2);
    sscanf(hex, "%x", c);
    bin2 = int_to_bin(c);
    c = get7bitchar(bin1, bin2);
    sprintf(str, "%s%c", str, c);
    bin1 = bin2;
    // Ueberlauf Behandlung
    if(strlen(bin1) >= 7)
    // if(strlen(bin1) > 7)
    {
      bin2 = "";
      c = get7bitchar(bin1, bin2);
      sprintf(str, "%s%c", str, c);
      bin1 = bin2;
    }
  }
  // Ende Behandlung
  if(strlen(bin1) > 0)
  {
    bin2 = "0000000";
    c = get7bitchar(bin1, bin2);
    sprintf(str, "%s%c", str, c);
  }
  return str;
}
// ---
// encode_ud_8bit
// ---
string  encode_ud_8bit
(
string  str
)
{
  string  ud_str = "";
  int     len, i;
  int     c;
  len = strlen(str);
  for (i = 0; i < len; i++)
  {
    c = str[i];
    sprintf(ud_str, "%s%0X", ud_str, c);
  }
  return ud_str;
}
// ---
// decode_ud_8bit
// ---
string  decode_ud_8bit
(
string  ud_str
)
{
  string  str = "";
  int     len, i;
  string  hex;
  int     c;
  len = strlen(ud_str);
  for (i = 0; i < len; i += 2)
  {
    hex = substr(ud_str, i, 2);
    sscanf(hex, "%x", c);
    sprintf(str, "%s%c", str, c);
  }
  return str;
}
// -----------------------------------------------------------------------------
// Zeichenkonvertierung
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// Konvertierung BCD
// -----------------------------------------------------------------------------
// ---
// encode_bcd
// ---
string  encode_bcd
(
string  str
)
{
  string  bcd_str = "";
  int     len, i;
  len = strlen(str);
  for (i = 0; i < len; i += 2)
  {
    if ((i == len - 1))
      bcd_str = bcd_str + 'F' + str[i];
    else
      bcd_str = bcd_str + str[i + 1] + str[i];
  }
  return bcd_str;
}
// ---
// decode_bcd
// ---
string  decode_bcd
(
string  bcd_str
)
{
  string  str = "";
  int     len, i;
  len = strlen(bcd_str);
  for (i = 0; i < len; i += 2)
  {
    if ((i == len - 2) && (bcd_str[i] == 'F'))
      str = str + bcd_str[i + 1];
    else
      str = str + bcd_str[i + 1] + bcd_str[i];
  }
  return str;
}
// -----------------------------------------------------------------------------
// Zusaetzliche Hilfsfunktionen
// -----------------------------------------------------------------------------
// ---
// Normalize_Quality
//
// Normierung von 0-31 nach 0-100
// 99 -> (-1)
// wie beim Siemens GSM Module M1
// ---
int Normalize_Quality
(
int   receive_level
)
{
  int         ret = (-1);
  if (receive_level == 99)
    ret = (-1);
  else
    ret = 100 * receive_level / 31;
  return ret;
}
// -----------------------------------------------------------------------------
// main - Connect mit _SMS-Datenpunkten
// -----------------------------------------------------------------------------
main()
{
  delay(3);
  string      dp;
  int         j;
  dyn_string  ds = dpNames("*", "_SMS");
  string sAdd;
  lst_lockid = 0;   //
    if(SMS_iniDP() != 0)
    {
      DebugN("Initialize SMS Error");
      return;
    }
  initHosts();
  if (getHostname() == host2)   // left host -> use right dpe
     sAdd = "_2";
  for (j = 1; j <= dynlen(ds); j++)
  {
    dp = ds[j];
    if(isReduDp(dp))
       continue;
    dp = dp + sAdd;
    if (!dpExists(dp))
       continue;
    dpSet(dp + ".DeviceReady:_original.._value", FALSE,
          dp + ".Quality:_original.._value",     0,
          dp + ".FileHandle:_original.._value",  -1 );
    // Unlock
    dpConnect(
        "init",
        dp + ".SMSC:_original.._value",
        dp + ".SIM:_original.._value",
        dp + ".ComPort:_original.._value",
        dp + ".BaudRate:_original.._value",
        dp + ".PortSetting:_original.._value"
        );
    dpConnect(
        "atwrite",
        0,
        dp + ".AT.SendDirect:_original.._value"
        );
    dpConnect(
        "writeWorkCB",
        0,
        dp + ".Out.SendTel:_original.._value",
        dp + ".Out.Msg:_original.._value",
        dp + ".Out.Msg:_original.._stime"
       );
    dpConnect(
        "memdel",
        dp + ".MemReset:_original.._value"
        );
    startThread("mainloop", dp);
    delay(10);
    SMS_Start();
  }
}
// ---
// init
// ---
init
(
string dp, string SMSC,
string dp1, string SIM,
string dp2, string ComPort,
string dp3, int BaudRate,
string dp4, string PortSetting
)
{
  int error_code;
  int handle;
  int receive_level;
  unsigned thr_lockid;
  unsigned act_lockid;
  int lockid;
  int SmscHeaderFlag;
  dp = dpSubStr(dp, DPSUB_SYS_DP);
  if ( SMSC == "" || ComPort == "")
  {
     dpSet(dp + ".Status:_original.._value", 101);   //  101,SMSC falsch
     return;
  }
  thr_lockid = ++lst_lockid;
  dpSet(dp + ".ReceiveActive:_original.._value", thr_lockid); // set lock !!!
  dpGet(dp + ".FileHandle:_original.._value", handle,
        dp + ".SmscHeader:_original.._value", SmscHeaderFlag);
  if (handle != (-1))
  {
   SMS_Exit(handle, error_code);
   dpSet(dp + ".FileHandle:_original.._value", -1);
   delay(1);
  }
  handle = (-1);  // zur Sicherheit
  SMS_Init(ComPort, BaudRate, PortSetting, SMSC, SIM, handle, error_code, SmscHeaderFlag, dp);
  dpSet(dp + ".FileHandle:_original.._value", handle,
        dp + ".Status:_original.._value", error_code);
  if (!error_code)
  {
    dpSet(dp + ".DeviceReady:_original.._value", TRUE);
  }
  else
  {
    dpSet(dp + ".DeviceReady:_original.._value", FALSE);
  }
  delay(1,100); v24Write(handle,"+++"); delay(0,600);
  SMS_Quality(handle, receive_level, error_code, SmscHeaderFlag);
  dpSet(dp + ".Quality:_original.._value", receive_level,
        dp + ".ReceiveActive:_original.._value", 0);   // Unlock
}
// ---
// writeWorkCB
// ---
writeWorkCB
(
string dp, string SendTel,
string dp1, string Msg,
string dp2, time t
)
{
  int error_code;
  int handle;
  int receive_level;
  int SmscHeaderFlag;
  unsigned thr_lockid;
  unsigned act_lockid;
 // SMS内容相同？ 且30s之内多次触发；
//   if ( Msg == sLastMessage && (t - tLastMessageTime) < iMessageBlockTime  )
//   {
//     DebugTN("SMS blocked: ", Msg);
//     return;
//   }
  sLastMessage  = Msg ;
  tLastMessageTime = t;
  dp = dpSubStr(dp, DPSUB_SYS_DP);
  thr_lockid = ++lst_lockid;
  dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
  while (act_lockid != thr_lockid)
  {
    delay(0,100); // 200 Millisekunden
    dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
      if (act_lockid == 0)
      dpSet(dp + ".ReceiveActive:_original.._value", thr_lockid);
  }
  dpGet(dp + ".FileHandle:_original.._value", handle,
        dp + ".SmscHeader:_original.._value", SmscHeaderFlag);
  if (handle == -1)
  {
    dpSet(dp + ".ReceiveActive:_original.._value", 0,   // Unlock & reset state
            dp + ".Out.MsgSent:_original.._value", FALSE,
            dp + ".DeviceReady:_original.._value", FALSE,
            dp + ".Status:_original.._value", -1,
            dp + ".Quality:_original.._value", 0);
      return;
  }
  SMS_Send(handle, SendTel, Msg, SmscHeaderFlag, error_code);
  // SMSC-Header == 0: bei Misserfolg 2. Sendeversuch starten
  if (!SmscHeaderFlag)
  {
    if (error_code)
    {
      // 2. Versuch mit SMSC-Header == 1 (Siemens M1-Bug)
      SmscHeaderFlag = 1;
      SMS_Send(handle, SendTel, Msg, SmscHeaderFlag, error_code);
      if (!error_code)
          dpSet(dp + ".SmscHeader:_original.._value", 1);          // TS 8.1.03 erst auf M1 Modus setzen, wenn WIRKLICH M1
    }
    else
    {
      dpSet(dp + ".SmscHeader:_original.._value", 2);
    }
  }
  if (!error_code)
  {
    dpSet(dp + ".Out.MsgSent:_original.._value", TRUE);
    dpSet(dp + ".DeviceReady:_original.._value", TRUE);
  }
  else if ((error_code == 106)) // Error: SMS senden nicht moeglich
  {
    dpSet(dp + ".Out.MsgSent:_original.._value", FALSE);
    dpSet(dp + ".DeviceReady:_original.._value", TRUE);
  }
  else
  {
    dpSet(dp + ".Out.MsgSent:_original.._value", FALSE);
    dpSet(dp + ".DeviceReady:_original.._value", FALSE);
  }
  dpSet(dp + ".Status:_original.._value", error_code);
  SMS_Quality(handle, receive_level, error_code, SmscHeaderFlag);
  dpSet(dp + ".Quality:_original.._value", receive_level,
        dp + ".ReceiveActive:_original.._value", 0);    // Unlock
}
// ---
// atwrite
// ---
atwrite
(
string dp, string SendDirect
)
{
  int error_code;
  int handle;
  dyn_string  sms_status_str;
  int receive_level;
  unsigned thr_lockid;
  unsigned act_lockid;
  int     SmscHeaderFlag;
  dp = dpSubStr(dp, DPSUB_SYS_DP);
  //
  thr_lockid = ++lst_lockid;
  dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
  while (act_lockid != thr_lockid)
  {
    delay(0,100); // 200 Millisekunden
    dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
      if (act_lockid == 0)
      dpSet(dp + ".ReceiveActive:_original.._value", thr_lockid);
  }
  //
  dpGet(dp + ".FileHandle:_original.._value", handle,
        dp + ".SmscHeader:_original.._value", SmscHeaderFlag );
  SMS_AT(handle, SendDirect, sms_status_str, error_code, SmscHeaderFlag);
  dpSet(dp + ".AT.SendDirectResult:_original.._value", sms_status_str,
        dp + ".DeviceReady:_original.._value", (!error_code?TRUE:FALSE),
        dp + ".Status:_original.._value", error_code);
  SMS_Quality(handle, receive_level, error_code, SmscHeaderFlag);
  dpSet(dp + ".Quality:_original.._value", receive_level,
        dp + ".ReceiveActive:_original.._value", 0);  // Unlock
}
// ---
// memdel
// ---
memdel
(
string dp, bool MemReset
)
{
  int         error_code;
  int         handle;
  dyn_string  sms_status_str;
  int         receive_level;
  //
  unsigned    thr_lockid;
  unsigned    act_lockid;
  int SmscHeaderFlag;
  //
  dp = dpSubStr(dp, DPSUB_SYS_DP);
  if (MemReset == FALSE)
    return;
  //
  thr_lockid = ++lst_lockid;
  dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
  while (act_lockid != thr_lockid)
  {
    delay(0,100); // 200 Millisekunden
    dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
      if (act_lockid == 0)
      dpSet(dp + ".ReceiveActive:_original.._value", thr_lockid);
  }
  //
  dpGet(dp + ".FileHandle:_original.._value", handle);
  SMS_Delete(handle, error_code, SmscHeaderFlag);
  dpSet(dp + ".Status:_original.._value", error_code);
  SMS_Quality(handle, receive_level, error_code, SmscHeaderFlag);
  dpSet(dp + ".Quality:_original.._value", receive_level,
        dp + ".ReceiveActive:_original.._value", 0,               // Unlock
        dp + ".MemReset:_original.._value", FALSE);               // wichtig hier.
}
// ---
// mainloop
// ---
mainloop
(
string  dp
)
{
  int         handle = (-1);
  int         autodelete;
  string      telnr;
  string      msg;
  string      msg_time;
  int         error_code;
  //
  int         SmscHeaderFlag;
  //
  time        mtime;
  //
  unsigned    thr_lockid;
  unsigned    act_lockid;
  //
  int         qualityCounter;
  int         errorCounter;
  int         receive_level;
  int        iStatus,  iQuality;
  bool       bReady;
  string     SMSC;
  while(1)
  {
    delay(10);
    if (debugOn) DebugTN("Loopcycle");
    //
    thr_lockid = ++lst_lockid;
    dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
    while (act_lockid != thr_lockid)
    {
      delay(0,100); // 100 Millisekunden
      dpGet(dp + ".ReceiveActive:_original.._value", act_lockid);
        if (act_lockid == 0)
        dpSet(dp + ".ReceiveActive:_original.._value", thr_lockid);
    }
    //
    dpGet(dp + ".FileHandle:_original.._value", handle,
          dp + ".AutoDelete:_original.._value", autodelete,
          dp + ".SmscHeader:_original.._value", SmscHeaderFlag);
    if (handle < 0)         // error
    {
       dpSet(dp + ".ReceiveActive:_original.._value", 0);
       delay(60);
       continue;
    }
    delay(1,100); v24Write(handle,"+++"); delay(0,600);
    SMS_Receive(handle, autodelete, SmscHeaderFlag, telnr, msg, msg_time, error_code);
    if (!error_code)
    {
      mtime = msg_time;
      dpSet(dp + ".In.RecvTel:_original.._value", telnr,
            dp + ".In.Msg:_original.._value", msg,
            dp + ".In.Time:_original.._value", mtime);
      // DebugTN("telnr", telnr);
      dpSet(dp + ".DeviceReady:_original.._value", TRUE);
    }
    else if ((error_code == 200))   //   200  keine SMS zum Lesen da
    {
      dpSet(dp + ".DeviceReady:_original.._value", TRUE);
    }
    else if ((error_code == 103)    //   103  SMS loeschen nicht moeglich
        || (error_code == 105))     //   105  SMS lesen nicht moeglich
    {
       dpSet(dp + ".DeviceReady:_original.._value", TRUE);
       errorCounter ++;
    }
    else
    {
      dpSet(dp + ".DeviceReady:_original.._value", FALSE);
    }
    if ((error_code != 200))        //   200  keine SMS zum Lesen da
    {
      dpSet(dp + ".Status:_original.._value", error_code);
    }
    if ( qualityCounter > 10)
    {
      SMS_Quality(handle, receive_level, error_code, SmscHeaderFlag);
      dpSet(dp + ".Quality:_original.._value", receive_level);
      qualityCounter = 0;
    }
    qualityCounter++;
    // Unlock
    dpSet(dp + ".ReceiveActive:_original.._value", 0);
    if ( error_code == 105 && errorCounter > 3 )  // SMS Lesen nicht m�glich, && mindestens 3 Fehlschl�ge -> RESET
    {
       dpGet( dp + ".SMSC:_online.._value", SMSC);
       errorCounter = 0;
       DebugTN("Automatic reset");
       dpSet( dp + ".SMSC:_original.._value",SMSC);
       delay(30);
    }
  }
}
//自动初始化所需要的 SMSArc,_SMS_1
//
bool SMS_iniDP()
{
  dyn_string dsType;
  dyn_string dsComment;
  dyn_string  dpGroupNames;
  bool bRes;
  bool bFound;
  int i,j;
  string dpName,  SMSgroupID,dummyString,qureyDpNames;
  bRes = FALSE;
  // 有SMSArc类型存在？
  dsType = dpTypes("SMSArc");
  if( dynlen(dsType) == 0 ) //not exist SMSArc, can't work, create DP Type.
  {
   bRes = SMS_createSMSDpType(); // 创建SMSArc类型
   if(bRes) DebugN("SMS_createSMSDPType error");
  }
   dyn_string  ds = dpNames("*", "_SMS"); // 创建SMS内部DP点
   if((dynContains(ds,"_SMS_1") <= 0))
   {
      int n = dpCreate("_SMS_1","_SMS");
      if(n != 0)
      {
        bRes = true;
        DebugN( "dpCreate _SMS_1 ERROR");
      }
   }

 return bRes;  //  TRUE = ERROR.
}
bool SMS_createSMSDpType()
{
      int n;
      dyn_dyn_string xxdepes;
      dyn_dyn_int xxdepei;
      // Create the data type
      xxdepes[1] = makeDynString ("SMSArc","");
      xxdepes[2] = makeDynString ("","smsArcTime");
      xxdepes[3] = makeDynString ("","smsArcText");
      xxdepes[4] = makeDynString ("","smsArcUser");
      xxdepes[5] = makeDynString ("","smsUserName");
      xxdepes[6] = makeDynString ("","smsTelephoneNumber");
      xxdepes[7] = makeDynString ("","smsEnable");
      xxdepes[8] = makeDynString ("","smsArcTel");
      xxdepes[9] = makeDynString ("","smsDescription");
      xxdepes[10] = makeDynString ("","smsUserGroup");
      xxdepes[11] = makeDynString ("","smsFilter");
      xxdepes[12] = makeDynString ("","","DP");
      xxdepes[13] = makeDynString ("","","String");
      xxdepes[14] = makeDynString ("","","UserGroup");
      xxdepes[15] = makeDynString ("","","Level");
      xxdepes[15] = makeDynString ("","smsArcMaxItems");
      xxdepei[1] = makeDynInt (DPEL_STRUCT);
      xxdepei[2] = makeDynInt (0,DPEL_DYN_TIME);
      xxdepei[3] = makeDynInt (0,DPEL_DYN_STRING);
      xxdepei[4] = makeDynInt (0,DPEL_DYN_STRING);
      xxdepei[5] = makeDynInt (0,DPEL_DYN_STRING);
      xxdepei[6] = makeDynInt (0,DPEL_DYN_STRING);
      xxdepei[7] = makeDynInt (0,DPEL_BOOL);
      xxdepei[8] = makeDynInt (0,DPEL_DYN_STRING);
      xxdepei[9] = makeDynInt (0,DPEL_STRING);
      xxdepei[10] = makeDynInt (0,DPEL_DYN_STRING);
      xxdepei[11] = makeDynInt (0,DPEL_STRUCT);
      xxdepei[12] = makeDynInt (0,0,DPEL_DYN_STRING);
      xxdepei[13] = makeDynInt (0,0,DPEL_DYN_STRING);
      xxdepei[14] = makeDynInt (0,0,DPEL_DYN_STRING);
      xxdepei[15] = makeDynInt (0,0,DPEL_DYN_STRING);
      xxdepei[15] = makeDynInt (0,DPEL_INT);
      n = dpTypeCreate(xxdepes,xxdepei);
      if(n!=0) return true;
      else return false;
}
SMS_Start()
{
   int n = dpQueryConnectSingle("SMS_workCB", 0, "ident",
                            "SELECT ALERT '_alert_hdl.._act_state_color', '_alert_hdl.._value', '_alert_hdl.._text' FROM 'M1_*' WHERE _LEAF",3000); // 捕捉报警DP
     if(n == 0)
    {
       DebugN("报警短信功能正在运行.");
    }
     else
     {
       DebugN("报警短信功能有误.");
     }
}
SMS_workCB(string ident, dyn_dyn_anytype ddaTable)
{
    //
    if(debugOn) DebugN(ddaTable);
    dyn_string items = dpNames("*", "SMSArc");
    for (int smsDpId = 1; smsDpId <= dynlen(items); smsDpId++)
    {
        string dpSMS =  items[smsDpId];

        if(debugOn) DebugN("报警组：" + dpSMS);
        // 1. dpe 2.time 3.color 4.value 5 text
        // 【廊坊M1】2022-01-24 18:22:52 M1_F2_IT01机房冷通道01温度过高报警 当前值：29℃
        // 1. 是否激活短信功能
        bool bSMSEnable;
        dpGet(dpSMS + ".smsEnable",bSMSEnable);    //bSMSEnable? ture = run, flase = stop.
        if(!bSMSEnable)
        {
            return; // 未激活，不发送
        }
        // 2. 报警等级
        dyn_string dsLevel;
        dpGet(dpSMS + ".smsFilter.Level",dsLevel );
        if(debugOn) DebugN("报警筛选条件Level：" + dsLevel);
        if(dynlen(dsLevel) == 0 )
        {
            return;
        }

        // 3. 筛选条件 - 用户组
        dyn_string dsUserGroupFilter;
        dpGet(dpSMS + ".smsFilter.UserGroup",dsUserGroupFilter );
        if(debugOn) DebugN("报警筛选条件 UserGroup：" , dsUserGroupFilter );
        if(dynlen(dsUserGroupFilter) == 0 )
        {
            if(debugOn) DebugN("(dynlen(dsUserGroupFilter) == 0 ), return");
            return;
        }
        // 4. 筛选条件-字段
        dyn_string dsString;
        dpGet(dpSMS + ".smsFilter.String", dsString);
        if(debugOn) DebugN("报警筛选条件String：" , dsString);
        // 5. 筛选条件-DP，空间及设备DP。
        dyn_string dsDPFilter;
        dpGet(dpSMS + ".smsFilter.DP", dsDPFilter);
        string text,textUnicode;
        string sErrorText;
        int error;
        string dpName;
        string strSend;
        // 逐条处理报警点位
        for (int iAlertId = 2; iAlertId <= dynlen(ddaTable); iAlertId++)
        {
            string dpe = ddaTable[iAlertId][1];
            if(debugOn) DebugN("for ddaTable->dpe : ", dpe);
            string sAlertColor = ddaTable[iAlertId][3]; // sAlertColor 是报警触发时候的报警颜色，用以区分报警的等级和来去的方向。

            // 报警是否在筛选条件-报警等级的范围之内？
            bool isInAlertLevel = false;
            for (int j = 1; j <= dynlen(dsLevel); j++)
            {
                if(strpos(sAlertColor, dsLevel[j])>0  )
                {
                    if(debugOn) DebugN("strpos(sAlertColor, dsLevel[j])>0");
                    isInAlertLevel = true;
                }
            }

            // 不在报警等级筛选范围之内，退出。
            if(!isInAlertLevel)
            {
                continue;
            }


            // 报警符合筛选DP？
            //
            if(dynlen(dsDPFilter) != 0) //
            {


              bool isInDpFilter = false;
              for (int j = 1; j <= dynlen(dsDPFilter); j++)
              {
                  string dpFilter =  dsDPFilter[j];
                  strreplace(dpFilter, "*", "");
                  if(strpos(dpe, dpFilter) >=0)
                  {
                      if(debugOn) DebugN("strpos(dpe, dpFilter) >=0");
                      isInDpFilter = true;
                      j = dynlen(dsDPFilter); // quit for(;;)
                  }
              }
              if(!isInDpFilter) // 不符合DP过滤条件
              {
                  continue;
              }
            }

            // 处理报警描述，查找是否符合报警过滤条件
            string dpDescription;
            if(dynlen(dsString) != 0) //有筛选条件(字段)存在
            {

                string isBITDp = dpe;
                if(strpos(isBITDp, "BIT")>0) // 针对拆位的点位，找到未拆位的DP，并获得描述字段
                {
                    strreplace(isBITDp, "sys1:", "");
                    dyn_string dsBit = strsplit(isBITDp, ".");
                    strreplace(isBITDp, dsBit[dynlen(dsBit)], "VALUE");
                    bool bRet = false;
                    for (int iStrId = 1; iStrId <= dynlen(dsString); iStrId++)
                    {
                        if(strpos(dpGetDescription(isBITDp), dsString[iStrId]  ) >=0)
                        {
                            bRet = true;
                        }
                    }
                    if(bRet)
                    {
                        dpDescription = ddaTable[iAlertId][5]; // BIT 报警自己的报警文本
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {

                    string sDes = ddaTable[iAlertId][5];
                    for (int iStrId = 1; iStrId <= dynlen(dsString); iStrId++)
                    {
                        if(strpos( sDes, dsString[iStrId]  ) >=0)
                        {
                            dpDescription = ddaTable[iAlertId][5];
                            if(debugOn) DebugN("strpos( sDes, dsString[iStrId]  )", sDes , dsString[iStrId] );
                            break;
                        }
                    }

                }
                if(dpDescription == "") // 未找到定义的筛选项，退出循环
                {
                   return;
                }
            }
            else // 未定义筛选字段
            {
                dpDescription = ddaTable[iAlertId][5];
            }

            // 未找到符合条件的字段筛选条件，不处理。
            if(strlen(dpDescription) == 0)
            {
                if(debugOn) DebugN("strlen(dpDescription) == 0");
                continue;
            }


            if(debugOn) DebugN(dpDescription);
            //////////////////////////
            dyn_string dsUsr, dsTel, dsUsrGroup;
            dyn_time   dsArcTime;
            dyn_string dsArcUser;
            dyn_string dsArcText;
            dyn_string dsArcTel;
            dpGet(dpSMS + ".smsUserName",dsUsr,
                  dpSMS + ".smsTelephoneNumber",dsTel,
                  dpSMS + ".smsUserGroup",dsUsrGroup
                 );
            for(int iUserId = 1; iUserId<=dynlen(dsUsrGroup); iUserId++)
            {
                // 用户属于激活的短信发送用户组 dsUserGroupFilter？
                if(!dynContains(dsUserGroupFilter, dsUsrGroup[iUserId]))
                {

                    if(debugOn) DebugN("if(!dynContains(dsUserGroupFilter, dsUsrGroup[iUserId]))", dsUserGroupFilter ,dsUsrGroup[iUserId] );
                    continue;
                }

                string sTag = dpe;
                strreplace(sTag, "_", "-");
                strreplace(sTag, "sys1:", "");
                time t = ddaTable[iAlertId][3];
                anytype value = ddaTable[iAlertId][4];
                string sValue = dpValToString(dpe, value, true);
                string sText = ddaTable[iAlertId][5];
                string sTime = formatTime("%Y-%m-%d %H:%M:%S", t);
                // strSend = "【廊坊M1】 " +sTime + " " + sTag  + " " + dpDescription + " 当前值：" + sValue ;
                strSend = "【廊坊M1】 "     + dpDescription + "：" + sValue ;
                sendSMS("+86" + dsTel[iUserId], strSend, error, sErrorText, "_SMS_1");
                dyn_time   dsArcTime;
                dyn_string dsArcUser;
                dyn_string dsArcText;
                dyn_string dsArcTel;
                dpGet(dpSMS + ".smsArcUser",dsArcUser,
                      dpSMS + ".smsArcTime",dsArcTime,
                      dpSMS + ".smsArcTel",dsArcTel,
                      dpSMS + ".smsArcText",dsArcText);
                int  pos=dynlen(dsArcUser);
                string sArcText = (error==0)? strSend + getCatStr("smsUnicode","smsSendDone"):strSend + getCatStr("smsUnicode","smsSendFailure");
                dynInsertAt(dsArcUser,dsUsr[iUserId],1);
                dynInsertAt(dsArcTime,getCurrentTime(),1);
                dynInsertAt(dsArcText, sArcText,1);
                dynInsertAt(dsArcTel, dsTel[iUserId],1);
                int smsArcMaxItems;
                dpGet( dpSMS + ".smsArcMaxItems", smsArcMaxItems);
                if(smsArcMaxItems == 0)
                {
                  smsArcMaxItems = 2000;
                }

                if(dynlen(dsArcUser)>smsArcMaxItems) // 发送历史记录保留2000（默认）条历史。
                {
                    dynRemove(dsArcUser, dynlen(dsArcUser));
                    dynRemove(dsArcTime, dynlen(dsArcTime));
                    dynRemove(dsArcText, dynlen(dsArcText));
                    dynRemove(dsArcTel, dynlen(dsArcTel));
                }
                dpSet(dpSMS + ".smsArcUser",dsArcUser,
                      dpSMS + ".smsArcTime",dsArcTime,
                      dpSMS + ".smsArcTel",dsArcTel,
                      dpSMS + ".smsArcText",dsArcText);
                string sErrorText = (error==0)?"OK":"Error" ;
                DebugTN("发送短信.\n " +
                        "内容:" + strSend + "\n" +
                        "接收者: " + dsUsr[iUserId] + "\n"
                        "电话:"+ dsTel[iUserId]+ "\n" +
                        "状态：" + sErrorText );
                delay(1);
            }

            iAlertId= dynlen(ddaTable); // 找到至少1个报警点，强制结束循环，防止多条记录多次触发报警。
        }
    }
}

void ANSI_Unicode(string message , blob &blobValue)
{

  system(getPath(BIN_REL_PATH) + "ANSI_UCS2.exe " + message, blobValue);
  // DebugN("SMS: ", message, "blobValue",blobValue);
}


// -----------------------------------------------------------------------------
// EOF
